/**
 * @fileoverview Simple implementation of HTML5 Validation.
 * @link http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
 * @link https://developers.google.com/closure/compiler/docs/js-for-compiler
 */



/**
 * Simple implementation of HTML5 validation for non-supported browsers.
 * @constructor
 * @requires dom.events
 * @example
 * (<b>new</b> forms.Validation).init(document.forms[0]);
 */
forms.Validation = function() {

  /**
   * Initializes HTML5 form validation feature.
   * @param {Element} container The HTML container which contains form elements.
   */
  this.init = function(container) {
    if (!isSupported_) {
      /** @type {Array.<string>} */ var tags = ['INPUT', 'TEXTAREA', 'SELECT'];
      for (/** @type {number} */ var i = 0; i < tags.length; i++) {
        setValidation_(dom.getElementsByTagName(container, tags[i]));
      }
    }
  };

  /**
   * @param {NodeList} elements List of HTML Form elements.
   * @private
   */
  function setValidation_(elements) {
    for (/** @type {number} */ var i = 0; i < elements.length; i++) {
      /** @type {Node|Element} */ var element = elements[i];
      initFieldValidation_(element);
      initFormValidation_(element.form);
    }
  }

  /**
   * @param {Node|Element} element HTML input or textarea element.
   * @private
   */
  function initFieldValidation_(element) {
    /**
     * A string containing the standard or custom error message that would
     * be displayed if the user submitted at this time.
     * @type {string}
     * @link http://msdn.microsoft.com/en-us/library/ie/hh772950
     */
    element['validationMessage'] = '';

    /**
     * Sets custom validity message.
     * @param {string} message String containing a custom message.
     * @link http://msdn.microsoft.com/en-us/library/ie/hh772949
     */
    element.setCustomValidity = function(message) {
      element['validationMessage'] = message;
    };

    element.checkValidity = function() { return checkFieldValidity_(element); };

    if (element.hasAttribute('required')) {
      element.className += ' required';
    }
  }

  /**
   * Initializes form validation.
   * @param {Node|Element} form The form element to initialize validation.
   * @private
   */
  function initFormValidation_(form) {
    if (form) {
      if (!form.checkValidity) {
        form.checkValidity = function() { return checkFormValidity_(form); };
      }

      if (!form.isAttached_) {
        dom.events.addEventListener(form, dom.events.TYPE.SUBMIT, function(e) {
          if (!form.checkValidity()) {
            dom.events.preventDefault(e);
            return false;
          }
          return true;
        });

        form.isAttached_ = true;
      }
    }
  }

  /**
   * @param {Node|Element} element HTML input or textarea element.
   * @return {boolean} Returns true if the form passes validation,
   *     false otherwise.
   * @link http://msdn.microsoft.com/en-us/library/ie/hh772948
   * @private
   */
  function checkFieldValidity_(element) {
    /** @type {string} */
    var placeholder = element.getAttribute('placeholder') || '';
    if (element.value == placeholder) {
      element.value = '';
    }

    if (element.hasAttribute('required') && !element.value) {
      element['validationMessage'] = 'Please fill out this field';
      element.value = placeholder;
      return false;
    }

    if (!checkTypeValidity_(element)) {
      return false;
    }

    if (element.hasAttribute('pattern') && element.value) {
      /** @type {!RegExp} */
      var re = new RegExp(element.getAttribute('pattern'), 'g');
      if (!re.test(element.value)) {
        element['validationMessage'] = 'The pattern is mismatched';
        return false;
      }
    }


    element['validationMessage'] = '';
    if (!element.value) element.value = placeholder;
    return true;
  }

  /**
   * Returns whether a form will validate when it is submitted, without having
   * to submit it.
   * @param {Node|Element} form The form element.
   * @return {boolean} Returns true if the form passes validation.
   * @link http://msdn.microsoft.com/en-us/library/ie/hh772948
   * @private
   */
  function checkFormValidity_(form) {
    /** @type {Element} */ var invalid = null;

    for (/** @type {number} */ var i = 0; i < form.elements.length; i++) {
      /** @type {Element} */ var element = form.elements[i];

      if (element.checkValidity && !element.checkValidity()) {
        if (!invalid) invalid = element;
      }

      if (!element.tooltip) {
        element.tooltip = dom.createElement('SPAN');
        element.tooltip.className = 'validation-tooltip';
        element.parentNode.insertBefore(element.tooltip, element);
        element.parentNode.insertBefore(element, element.tooltip);
      }
      element.tooltip.style.display = 'none';
    }

    if (invalid) {
      showValidationMessage_(invalid);
      return false;
    }
    return true;
  }

  /**
   * @param {Node|Element} element HTML input or textarea element.
   * @return {boolean} Returns true if element value is valid, false otherwise.
   * @private
   */
  function checkTypeValidity_(element) {
    if (element.getAttribute('type') == 'email' && element.value) {
      if (!(/^[\w\.\-]+@[\w\.\-]+\.[a-zA-Z]{2,4}$/g).test(element.value)) {
        element['validationMessage'] = 'Please enter a valid email address';
        return false;
      }
    }

    if (element.getAttribute('type') == 'url' && element.value) {
      if (!(/^((http|https):\/\/).*/g).test(element.value)) {
        element['validationMessage'] = 'Please enter a valid URL address';
        return false;
      }
    }
    return true;
  }

  /**
   * @param {Node|Element} element HTML element.
   * @private
   */
  function showValidationMessage_(element) {
    if (!showValidationMessage_.isAttached_) {
      dom.events.addEventListener(
          dom.document, dom.events.TYPE.MOUSEDOWN, function() {
            if (invalidElement_ && invalidElement_.tooltip) {
              invalidElement_.tooltip.style.display = 'none';
            }
          });

      showValidationMessage_.isAttached_ = true;
    }
    element.tooltip.innerHTML = element['validationMessage'];
    element.tooltip.style.display = 'block';
    element.focus();
    invalidElement_ = element;
  }

  /**
   * @type {boolean}
   * @private
   */
  var isSupported_ = 'required' in dom.createElement('INPUT');

  /**
   * @type {Node|Element}
   * @private
   */
  var invalidElement_ = null;
};
